<html>
<head>
<meta charset="utf-8">
<script src="../../lib/opencv.js"></script>
</head>
<body>
<h2>Moravec角点</h2>
<table border="1">
<tr>
    <th>参数</th>
    <th>值</th>
    <th>备注</th>
</tr>
<tr>
    <td>窗口尺寸：</td>
    <td><input type="number" value="3" id="input_windowSize1"/></td>
    <td>用于比较像素变化的窗口的大小</td>
<tr>
    <td>阈值：</td>
    <td><input type="number" value="10" id="input_thresh"/></td>
    <td>用于滤除较弱的角点</td>
</tr>
</table>
<br>

<canvas id="canvas" style="background:DarkTurquoise "> </canvas>
<canvas id="canvas2" style="background:DeepSkyBlue "> </canvas><br>
<input id="chooseImage" type="file" accept="image/*"></input> <br>
<button id="button_run" onclick="moravec_corners(canvas)">运行</button>
<br><br>

<h4>参考链接</h4>
<li><a href="https://blog.csdn.net/f290131665/article/details/80064479">特征算法之Moravec算法</a></li>


<script>
/*
TODO:
1. 考虑加入一些预处理步骤；
2. 加入非极大值抑制。
*/
const canvas = document.getElementById('canvas')
const canvas2 = document.getElementById('canvas2')
const input = document.getElementById('chooseImage')

// 图像的加载
function onImageLoaded(e) {
    const ctx = canvas.getContext('2d')
    console.log('图像加载完成')
    const image = e.target
    canvas.width = image.width
    canvas.height = image.height
    ctx.drawImage(image, 0, 0)
}

input.addEventListener('change', function(e) {
    const fileReader = new FileReader()
    fileReader.onload = function(e) {
        console.log('文件读取完成')
        const imgFile = e.target.result
        const image = new Image()
        image.onload = onImageLoaded
        image.src = imgFile
    }    
    fileReader.readAsDataURL(input.files[0])
})


function assert(value, msg) {
    if(!value) {
        if (msg)
            alert('程序出错：' + msg)
        else
            alert('程序遇到问题，停止运行！')
        throw new Object('assertion failed!')
    }
}


// 求Moravec角点
function moravec_corners() {
    // 读取参数并检查
    const windowSize1 = parseInt(document.getElementById('input_windowSize1').value)
    assert(windowSize1 > 0, '窗口尺寸必须大于0')
    assert(windowSize1 % 2 == 1, '窗口尺寸必须是奇数')
    const thresh = parseInt(document.getElementById('input_thresh').value)
    assert(thresh >= 0 && thresh <= 255, "阈值超出合理范围！")

    // 转为单通道图像
    const imgRaw = cv.imread(canvas)
    const imgGray = new cv.Mat()
    cv.cvtColor(imgRaw, imgGray, cv.COLOR_BGR2GRAY)
    const width = imgRaw.cols
    const height = imgRaw.rows
    assert(windowSize1 < Math.min(width, height), '窗口尺寸大过图像尺寸！')
    cv.imshow(canvas2, imgGray)

    // 计算往四个方向偏移的图像
    const imgCenter = imgGray.roi(new cv.Rect(1, 1, width - 2, height - 2))
    function imgIntegral(offsetX, offsetY) {
        // 计算变化量平方的积分图
        const imgOffset = imgGray.roi(new cv.Rect(offsetX, offsetY, width - 2, height - 2))
        const d = new cv.Mat()
        cv.subtract(imgCenter, imgOffset, d, new cv.Mat(), cv.CV_32FC1)
        const square = new cv.Mat()
        cv.multiply(d, d, square, 1, cv.CV_32FC1)
        const integral = new cv.Mat()
        cv.integral(square, integral, cv.CV_64FC1)
        return integral
    }
    const imgIntegralList = [
        imgIntegral(1, 2),
        imgIntegral(2, 2),
        imgIntegral(2, 1),
        imgIntegral(2, 0)
    ]
    function ssd(integral, row, col) {
        // 因为事先算好了积分图，所以可以容易算出ssd的值
        s1 = integral.doubleAt(row, col)
        s2 = integral.doubleAt(row + windowSize1, col)
        s3 = integral.doubleAt(row, col + windowSize1)
        s4 = integral.doubleAt(row + windowSize1, col + windowSize1)
        return s4 + s1 - s2 - s3
    }

    // 以窗口遍历图像
    const imgActivation = new cv.Mat(height - windowSize1 - 2, width - windowSize1 - 2, cv.CV_8UC1)
    for (let colStart = 1; colStart < width - windowSize1 - 1; colStart++) {
        for (let rowStart = 1; rowStart < height - windowSize1 - 1; rowStart++) {
            const activationList = []
            for(let k = 0; k < 4; k++) {
                activationList.push(ssd(imgIntegralList[k], rowStart, colStart))
            }
            const min = Math.min.apply(null, activationList)
            imgActivation.ucharPtr(rowStart - 1, colStart - 1)[0] = min
        }
    }
    cv.imshow(canvas2, imgActivation)

    // 滤除较弱的角点
    const img_thresh = new cv.Mat()
    cv.threshold(imgActivation, img_thresh, thresh, 255, cv.THRESH_TOZERO)
    cv.imshow(canvas2, img_thresh)
}
</script>
</body>